#include "clogger.h"

string CLogger::OUTPUT_INDENT = "  ";

/**
 * CLogger - constructor
 * @param priorMin
 * @param keepMemory
 * @param maxLocalMemory
 */
CLogger::CLogger(int priorMin, bool keepMemory, unsigned int maxLocalMemory) {
  toFile = false;
  toMemory = keepMemory;
  toStream = false;
  logDepth = 0;
  memoryMax = maxLocalMemory;
  memoryUsed = 0;
  priorPrint = priorMin;
}
//---------------------------------------------------------------------------

/**
 * CLogger - constructor, prints output to stream
 * @param out
 * @param priorMin
 * @param keepMemory
 * @param maxLocalMemory
 */
CLogger::CLogger(ostream &out, int priorMin, bool keepMemory,
    unsigned int maxLocalMemory) {
  toFile = false;
  toMemory = keepMemory;
  toStream = true;
  logDepth = 0;
  memoryMax = maxLocalMemory;
  memoryUsed = 0;
  priorPrint = priorMin;
  stream.copyfmt(out);
  stream.clear(out.rdstate());
  stream.basic_ios<char>::rdbuf(out.rdbuf());
}
//---------------------------------------------------------------------------

/**
 * CLogger - constructor, prints output to file
 * @param path
 * @param priorMin
 * @param keepMemory
 * @param maxLocalMemory
 */
CLogger::CLogger(string path, int priorMin, bool keepMemory,
    unsigned int maxLocalMemory) {
  toFile = true;
  toMemory = keepMemory;
  toStream = false;
  logDepth = 0;
  memoryMax = maxLocalMemory;
  memoryUsed = 0;
  priorPrint = priorMin;
  file.open(path.c_str(), ios_base::out | ios_base::trunc);
  if (!file.is_open()) {
    throw CFileNotExistException(getClassName(), "CLogger");
  }
}
//---------------------------------------------------------------------------

/**
 * ~CLogger - destructor, cleans memory and closes file
 */
CLogger::~CLogger() {
  if (file.is_open()) {
    file.close();
  }
  if (memory.size()) {
    clearLog();
  }
}
//---------------------------------------------------------------------------

/**
 * addMsg - adds message
 * @param str
 * @param priority
 */
void CLogger::addMsg(string str, int priority) {

  // Compare priority levels
  if (priorPrint > priority) {
    return;
  }

  // Add message to memory
  if (toMemory) {
    if (memoryUsed >= memoryMax) {
      clearLog();
    }
    TMessage tmp;
    tmp.depth = logDepth;
    tmp.prior = priority;
    tmp.text = str;
    memory.push_back(tmp);
    memoryUsed += sizeof(tmp.depth) + sizeof(tmp.prior) + sizeof(tmp.text);
  }

  // Print message to file
  if (toFile) {
    for (unsigned int i = 0; i < logDepth; i++) {
      file << OUTPUT_INDENT;
    }
    file << printPriorSign(priority) << str << endl;
  }

  // Print message to stream
  else {
    if (toStream) {
      for (unsigned int i = 0; i < logDepth; i++) {
        stream << OUTPUT_INDENT;
      }
      stream << printPriorSign(priority) << str << endl;
    }
  }
}
//---------------------------------------------------------------------------

/**
 * addDbg - adds debug message
 * @param str
 */
void CLogger::addDbg(string str) {
  addMsg(str, PRIORITY_DEBUG);
}
//---------------------------------------------------------------------------

/**
 * addInf - adds info message
 * @param str
 */
void CLogger::addInf(string str) {
  addMsg(str, PRIORITY_INFO);
}
//---------------------------------------------------------------------------

/**
 * addWrn - adds warning message
 * @param str
 */
void CLogger::addWrn(string str) {
  addMsg(str, PRIORITY_WARNING);
}
//---------------------------------------------------------------------------

/**
 * addErr - adds error message
 * @param str
 */
void CLogger::addErr(string str) {
  addMsg(str, PRIORITY_ERROR);
}
//---------------------------------------------------------------------------

/**
 * addCrt - adds critical error message
 * @param str
 */
void CLogger::addCrt(string str) {
  addMsg(str, PRIORITY_CRITICAL);
}
//---------------------------------------------------------------------------

/**
 * beginSubLvl - begins nested message level
 */
void CLogger::beginSubLvl() {
  logDepth++;
}
//---------------------------------------------------------------------------

/**
 * endSubLvl - ends nested message level
 */
void CLogger::endSubLvl() {
  logDepth--;
}
//---------------------------------------------------------------------------

/**
 * clearLog -clears logger memory
 */
void CLogger::clearLog() {
  memory.erase(memory.begin(), memory.end());
  memoryUsed = 0;
}
//---------------------------------------------------------------------------

/**
 * saveMemoryToFile - prints content of memory into file
 * @param path
 * @param type
 */
void CLogger::saveMemoryToFile(string path, int type) {

  // Open file for writing
  ofstream fl(path.c_str(), ios_base::out | ios_base::trunc);

  // If file is not open, throw exception
  if (!fl.is_open()) {
    throw CFileNotExistException(getClassName(), "saveMemoryToFile");
  }

  // Print memory into HTML file
  if (type == MEMORY_FILE_TYPE_HTML) {

    // Print HTML necessary tags
    fl << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
        << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
        << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" << endl
        << "<html xmlns=\"http://www.w3.org/1999/xhtml\">" << endl
        << "<head></head>" << endl << "<body>" << endl << "<ol>" << endl;

    // Print messages
    vector<TMessage>::iterator it;
    for (it = memory.begin(); it < memory.end(); it++) {

      // Format the output
      if (it == memory.begin()) {
        fl << "<li>" << endl;
      } else {
        if ((*it).depth > (*(it - 1)).depth) {
          fl << "<li>" << endl << "<ol>" << endl << "<li>" << endl;
        } else {
          if ((*it).depth < (*(it - 1)).depth) {
            fl << "</ol>" << endl << "</li>" << endl << "<li>" << endl;
          } else {
            fl << "<li>" << endl;
          }
        }
      }
      fl << ((*it).prior >= PRIORITY_ERROR ? "<strong>"+printMsg(it)+"</strong>"
          : printMsg(it));
      fl << endl << "</li>";
    }

    // Print HTML ending tags
    fl << "</ol>" << endl << "</body>" << endl << "</html>" << endl;
  }

  // Print memory into plain text file
  else {

    // Format the output
    stringstream tmp(ios_base::out | ios_base::in);
    saveMemoryToStream(tmp);

    // Print into file
    fl << tmp.str();
  }

  // Close file
  fl.close();
}
//---------------------------------------------------------------------------

/**
 * saveMemoryToStream - prints memory into stream
 * @param out
 */
void CLogger::saveMemoryToStream(ostream &out) {
  vector<TMessage>::iterator it;
  for (it = memory.begin(); it < memory.end(); it++) {
    out << printMsg(it) << endl;
  }
}
//---------------------------------------------------------------------------

/**
 * printMsg - prints message from memory
 * @param it
 * @return formated message
 */
string CLogger::printMsg(vector<TMessage>::iterator it) {
  string tmp;
  for (unsigned int i = 0; i < (*it).depth; i++) {
    tmp += OUTPUT_INDENT;
  }
  tmp += printPriorSign((*it).prior);
  tmp += (*it).text;
  return tmp;
}
//---------------------------------------------------------------------------

/**
 * printPriorSign - returns sign of chosen priority
 * @param prior
 * @return sign
 */
string CLogger::printPriorSign(int prior) {
  switch (prior) {
    case PRIORITY_DEBUG:
      return "Dbg : ";
    case PRIORITY_INFO:
      return "Note : ";
    case PRIORITY_WARNING:
      return "Warn : ";
    case PRIORITY_ERROR:
      return "Err : ";
    case PRIORITY_CRITICAL:
      return "Crit : ";
    default:
      return "Err : ";
  }
}
//---------------------------------------------------------------------------
